package com.ding.easycontroller.work;


import jdk.nashorn.api.scripting.ScriptObjectMirror;
import lombok.Data;

import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptException;
import java.util.regex.Pattern;


/*

    拦截器中必须包含  _before 或者 _after 或者 _default 中的某一个方法

    如果方法的返回值等价为false 则阻断.如果没有返回值或返回值等价为true则继续

    前置拦截器按照  order 从小到大遍历,后置拦截器则相反

    可以在同一个filter同时声明 _before和_after两个方法

    _default 用于当没有controller的拦截

 */
@Data
public class Filter extends Script {


    {
        setType("filter");
    }

    private int order = 0;

    private ScriptEngine scriptEngine;

    private String beforeJs;
    private String afterJs;
    private String defaultJs;

    private String beforeFunctionName;
    private String afterFunctionName;
    private String defaultFunctionName;

    //脚本中必须有一个
    private static Pattern beforeFilter = Pattern.compile("function[ \t\n]*_before[ \t\n]*\\([ \t\n]*\\)");
    private static Pattern afterFilter = Pattern.compile("function[ \t\n]*_after[ \t\n]*\\([ \t\n]*\\)");
    private static Pattern defaultFilter = Pattern.compile("function[ \t\n]*_default[ \t\n]*\\([ \t\n]*\\)");


    @Override
    protected void doInit() {

        if (beforeJs == null && afterJs == null && defaultJs == null) {
            preHandle();
        }

        if (beforeJs != null) {
            try {
                scriptEngine.eval(beforeJs);
            } catch (ScriptException e) {
                throw new RuntimeException(e);
            }
        }

        if (afterJs != null) {
            try {
                scriptEngine.eval(afterJs);
            } catch (ScriptException e) {
                throw new RuntimeException(e);
            }
        }
        if (defaultJs != null) {
            try {
                scriptEngine.eval(defaultJs);
            } catch (ScriptException e) {
                throw new RuntimeException(e);
            }
        }

    }

    @Override
    protected void doDestroy() {

        try {
            scriptEngine.eval("delete " + defaultFunctionName);
            scriptEngine.eval("delete " + afterFunctionName);
            scriptEngine.eval("delete " + beforeFunctionName);
        } catch (ScriptException e) {
            e.printStackTrace();
        }

    }

    private void preHandle() {

        if (js == null) {
            js = "";
        }

        boolean hasBefore = beforeFilter.matcher(js).find();
        boolean hasAfter = afterFilter.matcher(js).find();
        boolean hasDefault = defaultFilter.matcher(js).find();

        if (!(hasAfter || hasBefore || hasDefault)) {
            throw new RuntimeException("至少具备一个_before、_after或_default方法");
        }

        if (hasBefore) {
            beforeFunctionName = "filter_before_" + id;
            String pre = "function " + beforeFunctionName + "(_req,_resp,_reqHeads,_reader,_writer,_input,_output,_session,_params,_pathValues,_respHeads){var _status;\n";
            String post = "\nvar __r=_before();if(__r==undefined){__r = true}\nif(__r){__r = true}else{__r = false}\n" +
                    "return {result:__r,status:_status}}";
            beforeJs = pre + js + post;
        }

        if (hasAfter) {
            afterFunctionName = "filter_after_" + id;
            String pre = "function " + afterFunctionName + "(_req,_resp,_reqHeads,_reader,_writer,_input,_output,_session,_params,_pathValues,_respHeads){var _status;\n";
            String post = "\nvar __r=_after();if(__r==undefined){__r = true}\nif(__r){__r = true}else{__r = false}\n" +
                    "return {result:__r,status:_status}}";
            afterJs = pre + js + post;
        }

        //_pathValues为空
        if (hasDefault) {
            defaultFunctionName = "filter_default_" + id;
            String pre = "function " + defaultFunctionName + "(_req,_resp,_reqHeads,_reader,_writer,_input,_output,_session,_params,_pathValues,_respHeads){var _status;\n";
            String post = "\nvar __r=_default();if(__r==undefined){__r = true}\nif(__r){__r = true}else{__r = false}\n" +
                    "return {result:__r,status:_status}}";
            defaultJs = pre + js + post;
        }


    }


    public Result doBeforeFilter(Object[] params) {
        return f(params, beforeFunctionName);
    }

    public Result doAfterFilter(Object[] params) {
        return f(params, afterFunctionName);
    }

    public Result doDefaultFilter(Object[] params) {
        return f(params, defaultFunctionName);
    }

    private Result f(Object[] params, String functionName) {
        try {

            ScriptObjectMirror obj = (ScriptObjectMirror) ((Invocable) scriptEngine).invokeFunction(functionName, params);

            return new Result(obj);
        } catch (ScriptException | NoSuchMethodException e) {
            e.printStackTrace();
            return null;
        }
    }

    public boolean equals(Object obj) {
        return super.equals(obj);
    }


    public static class Result {
        private boolean goOn = false;
        private Integer status;

        private Result(ScriptObjectMirror obj) {
            this.goOn = (boolean) obj.get("result");
            Object status = obj.get("status");

            if (status != null) {

                if (!(status instanceof Integer)) {
                    throw new RuntimeException("响应码必须为数字");
                }
                this.status = (int) status;
            }
        }

        public boolean isGoOn() {
            return goOn;
        }

        public Integer getStatus() {
            return status;
        }


    }

}
